package com.lyw.i07deadLock;

/**
 * 1、死锁：指两个或两个以上的线程在执行过程中，因争夺资源而造成的一种互相等待的现象，若无外力
 *    作用，它们都将无法推进下去。
 *
 * 2、案例：
 *    线程 A 在持有锁 AL 的时候想要去获取线程 B（持有 BL 锁）的 BL 锁，而线程 B
 *    此时也想着获取线程 AL 锁就会造成死锁
 *
 * 3、产生死锁的原因：
 *    - 系统资源不足
 *    - 进程推进的顺序不合适
 *    - 资源分配不当
 *
 * 3.1、死锁的四个条件
 *    - 互斥条件：指线程对已经获取到的资源进行排他性使用，即该资源同时只由一个线程占用。
 *      如果此时还有其他线程请求获取该资源，则请求者只能等待，直至占有资源的线程释放该资源。
 *
 *    - 请求并持有条件：指一个线程已经持有了至少一个资源，但又提出了新的资源请求，而新资源已被
 *      其他线程占有，所以当前线程会被阻塞，但阻塞的同时并不释放自己已经获取的资源。
 *
 *    - 不可剥夺条件：指线程获取到的资源在自己使用完之前不能被其他线程抢占，只有在自己使用完毕
 *      后才由自己释放该资源。
 *
 *    - 环路等待条件：指在发生死锁时，必然存在一个线程——资源的环形链，即线程集合 {T0, T1, T2, … ,Tn}
 *      中的T0正在等待一个 T1 占用的资源， T1 正在等待 T2 占用的资源， … Tn 正在等待已被 T0 占用的资源。
 *
 *    总结：
 *      （1）“两个或两个以上的进程”：也就是说死锁必定是在并发“进程”（这里的进程可以看作是一个泛指，
 *      具体要看这个系统是什么）间产生，单个进程不会产生死锁。（2）“资源争夺”：这些并发的进程
 *      必须互相争夺资源，如果一个系统存在并发的进程，但是大家都井水不犯河水，那么也不会产生死
 *      锁。（3）造成的结果是这些进程进入一种状态，各个进程都无法继续推进，如果没有外力，这些
 *      进程无法跳出这个状态。
 *
 *  4、验证死锁
 *    - jps -l （类似 linux 的 ps -ef）
 *      13138 com.lyw.deadLock.DeadLockDemo
 *    - jstack + 上面指令的 id (jvm 自带的堆栈工具)
 *      jstack 13138
 *      这时候最后一行 Found 1 deadlock.
 *
 *  5、如何避免 ？
 *    要想避免死锁，只要破坏掉至少一个构造死锁的必要条件即可。
 *    造成死锁的原因其实和申请资源的顺序有很大关系，使用资源申请的有序性原则就可以避免死锁。
 *
 * @author lyw
 * @Date 2023/7/10-下午2:19
 */
public class DeadLockDemo {
  /**
   * 创建资源
   */
  private static final Object resourceA = new Object();
  private static final Object resourceB = new Object();

  public static void main(String[] args) {
    //创建线程A
    Thread threadA = new Thread(new Runnable() {
      public void run() {
        synchronized (resourceA) {
          System.out.println(Thread.currentThread() + " get resourceA");
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(Thread.currentThread() + " wait get resourceB");
          synchronized (resourceB) {
            System.out.println(Thread.currentThread() + " get resourceB");
          }
        }
      }
    });

    //创建线程B
    Thread threadB = new Thread(new Runnable() {
      public void run() {
        synchronized (resourceB) {
          System.out.println(Thread.currentThread() + " get resourceB");
          try {
            Thread.sleep(1000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(Thread.currentThread() + " wait get resourceA");
          synchronized (resourceA) {
            System.out.println(Thread.currentThread() + " get resourceA");
          }
        }
      }
    });

    //启动线程
    threadA.start();
    threadB.start();
  }
}
